package com.gagakuai.bpm.service.camunda.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.exceptions.ApiException;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;


import com.gagakuai.bpm.dao.mapper.camunda.WorkflowDefinitionMapper;
import com.gagakuai.bpm.domian.dto.camunda.WorkflowDefinitionDto;
import com.gagakuai.bpm.domian.dto.request.PageRequest;
import com.gagakuai.bpm.domian.dto.response.PageData;
import com.gagakuai.bpm.domian.entity.camuda.WorkflowDefinitionEntity;
import com.gagakuai.bpm.domian.query.camunda.WorkflowQuery;
import com.gagakuai.bpm.service.camunda.WorkflowDefinitionService;
import com.gagakuai.bpm.utils.ObjectUtils;
import com.gagakuai.bpm.utils.ResponseUtils;
import com.gagakuai.bpm.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/*
 * bpm
 */
@Service
@Slf4j
public class WorkflowDefinitionServiceImpl extends ServiceImpl<WorkflowDefinitionMapper, WorkflowDefinitionEntity> implements WorkflowDefinitionService {

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private HistoryService historyService;

    @Override
    public PageData<WorkflowDefinitionDto> listDefinitionByPage(PageRequest<WorkflowDefinitionDto> pageRequest) {
        WorkflowDefinitionDto condition = pageRequest.getCondition();
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();

        if(ObjectUtils.isNotEmpty(condition)){
            String tenantId  = condition.getTenantId();

            if(StringUtils.isNotBlank(tenantId)){
                query.tenantIdIn(tenantId);
            }

            // 如果versionTag为latest则查最新的流程定义
            if (StringUtils.equalsIgnoreCase(condition.getVersionTag(), "latest")) {
                query.latestVersion();
            }
            // 根据流程名称查询
            if (StringUtils.isNotBlank(condition.getName())) {
                query.processDefinitionNameLike("%".concat(condition.getName()).concat("%"));
            }
        }

        List<ProcessDefinition> list = query.listPage(pageRequest.getStart(0), pageRequest.getRows());
        long total = query.count();
        List<WorkflowDefinitionDto> definitionDtos = new ArrayList<>();
        list.forEach(def -> {
            WorkflowDefinitionDto dto = new WorkflowDefinitionDto();
            dto.setId(def.getId()).setKey(def.getKey())
                    .setName(def.getName())
                    .setVersion(def.getVersion())
                    .setVersionTag(def.getVersionTag())
                    .setCategory(def.getCategory())
                    .setDeploymentId(def.getDeploymentId())
                    .setResourceName(def.getResourceName())
                    .setDiagramResourceName(def.getDiagramResourceName())
                    .setTenantId(def.getTenantId());
            definitionDtos.add(dto);
        });
        PageData<WorkflowDefinitionDto> pageData = new PageData<>(pageRequest);
        pageData.setTotal(total).calcPages().setContent(definitionDtos);
        return pageData;
    }

    @Override
    public PageData<WorkflowDefinitionEntity> listProcessByPage(PageRequest<WorkflowQuery> pageRequest) {
        WorkflowQuery condition = pageRequest.getCondition();
        QueryWrapper<WorkflowDefinitionEntity> queryWrapper = new QueryWrapper<>();

        if(StringUtils.isNotBlank(condition.getTenantId())){
            queryWrapper.eq("t1.TENANT_ID_", condition.getTenantId());
        }
        // 根据流程名称模糊查询
        queryWrapper.like(StringUtils.isNotBlank(condition.getProcessDefinitionName()),
                "t1.NAME_", condition.getProcessDefinitionName());
        // 根据候选组查询
        if (StringUtils.isNotBlank(condition.getCandidateGroups())) {
            String instr = StringUtils.getSqlInStringByArray(condition.getCandidateGroups());
            queryWrapper.exists(String.format("select ID_ from act_ru_identitylink t3 " +
                    "where t3.PROC_DEF_ID_ = t1.ID_ and t3.GROUP_ID_ in (%s) ", instr));
        }
        // 根据候选用户查询
        if (StringUtils.isNotBlank(condition.getCandidateUsers())) {
            String instr = StringUtils.getSqlInStringByArray(condition.getCandidateUsers());
            queryWrapper.exists(String.format("select ID_ from act_ru_identitylink t3 " +
                    "where t3.PROC_DEF_ID_ = t1.ID_ and t3.USER_ID_ in (%s) ", instr));
        }
        IPage<WorkflowDefinitionEntity> page = pageRequest.buildMybatisPlusPage();
        IPage<WorkflowDefinitionEntity> iPage = this.baseMapper.listByPage(page, queryWrapper);
        return new PageData<>(iPage);
    }

    @Override
    public void deleteProcessDefinition(WorkflowDefinitionDto dto) {
        repositoryService.deleteProcessDefinition(dto.getId(), true);
    }

    @Override
    public void downloadResourceFile(WorkflowDefinitionDto dto){
        try {
            // 获取流程模型流，bpmn.xml内容
            InputStream inputStream = repositoryService.getProcessModel(dto.getId());
            // 输出到响应
            ResponseUtils.writeStream(ResponseUtils.getResponse(), dto.getResourceName(), inputStream);
            inputStream.close();
        } catch (Exception e) {
            throw new ApiException("下载资源失败！");
        }
    }

    @Override
    public String getDefinitionXmlString(String id) {
        try {
            InputStream inputStream = repositoryService.getProcessModel(id);
            StringWriter writer = new StringWriter();
            IOUtils.copy(inputStream, writer, "utf-8");
            String xml = writer.toString();
            writer.close();
            inputStream.close();
            return xml;
        } catch (Exception e) {
            throw new ApiException("获取bpmn xml字符串失败");
        }
    }

    @Override
    public String getDefinitionXmlByInsId(String id) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(id)
                .singleResult();

        if(ObjectUtils.isEmpty(historicProcessInstance)){
            return null;
        }
        return getDefinitionXmlString(historicProcessInstance.getProcessDefinitionId());
    }

    @Override
    public boolean saveBatch(Collection<WorkflowDefinitionEntity> entityList) {
        return false;
    }
}
